using System;
using System.Collections.Generic;
using System.Linq;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
using System.Reflection;
using Apache.Cassandra;


namespace Cassandraemon
{


	class CassandraEntityConverterBase<TInput, TOutput>
	{
		protected CassandraEntity<TInput> Entity { get; set; }
		
		protected TOutput Object { get; set; }
		
		protected Type Type { get; set; }
		

		public CassandraEntityConverterBase (CassandraEntity<TInput> entity)
		{
			Entity = entity;
			
			Object = (TOutput)Activator.CreateInstance(typeof(TOutput));
			Type = typeof(TOutput);
		}
		
		protected List<Column> GetFlatColumnList(TInput input)
		{
			List<Column> columns = null;
			
			Type type = input.GetType();
			
			if(type.Equals(typeof(Column)))
			{
				columns = new List<Column>(new Column[] { input as Column });
			}
			else if(type.Equals(typeof(SuperColumn)))
			{
				columns = (input as SuperColumn).Columns;
			}
			else if(type.Equals(typeof(List<Column>)))
			{
				columns = input as List<Column>;
			}
			else
			{
				columns = new List<Column>();
				var superColumns = (input as List<SuperColumn>);
				superColumns.ForEach(x => columns.AddRange(x.Columns));
			}
			
			return columns;
		}
		
		protected List<T> GetFlatNameList<T>(TInput input)
		{
			return GetFlatList(input, 
			                   x => MappingUtil.MappingValueType<T>(x.Name), 
			                   y => MappingUtil.MappingValueType<T>(y.Name));
		}
		
		protected List<T> GetFlatValueList<T>(TInput input)
		{
			return GetFlatList(input, 
			                   x => MappingUtil.MappingValueType<T>(x.Value), 
			                   y => (T)(object)y.Value);
		}
		
		protected List<KeyValuePair<TKey, TValue>> GetFlatKeyValueList<TKey, TValue>(TInput input)
		{
			return GetFlatList(input, 
			                   x => new KeyValuePair<TKey, TValue>(MappingUtil.MappingValueType<TKey>(x.Name), MappingUtil.MappingValueType<TValue>(x.Value)),
			                   y => new KeyValuePair<TKey, TValue>(MappingUtil.MappingValueType<TKey>(y.Name), (TValue)(object)y.Value));
		}
		
		private List<T> GetFlatList<T>(TInput input, Func<Column, T> columnFormatter, Func<CounterColumn, T> counterColumnFormatter)
		{
			Type type = input.GetType();
			
			if(type.Equals(typeof(Column)) ||
			   type.Equals(typeof(SuperColumn)) ||
			   type.Equals(typeof(List<Column>)) ||
			   type.Equals(typeof(List<SuperColumn>)))
			{
				List<Column> columns = null;
			
				if(type.Equals(typeof(Column)))
				{
					columns = new List<Column>(new Column[] { input as Column });
				}
				else if(type.Equals(typeof(SuperColumn)))
				{
					columns = (input as SuperColumn).Columns;
				}
				else if(type.Equals(typeof(List<Column>)))
				{
					columns = input as List<Column>;
				}
				else
				{
					columns = new List<Column>();
					var superColumns = (input as List<SuperColumn>);
					superColumns.ForEach(x => columns.AddRange(x.Columns));
				}
				
				return columns.Select(columnFormatter).ToList();
			}
			else
			{
				List<CounterColumn> columns = null;
				
				if(type.Equals(typeof(CounterColumn)))
				{
					columns = new List<CounterColumn>(new CounterColumn[] { input as CounterColumn });
				}
				else if(type.Equals(typeof(CounterSuperColumn)))
				{
					columns = (input as CounterSuperColumn).Columns;
				}
				else if(type.Equals(typeof(List<CounterColumn>)))
				{
					columns = input as List<CounterColumn>;
				}
				else
				{
					columns = new List<CounterColumn>();
					var superColumns = (input as List<CounterSuperColumn>);
					superColumns.ForEach(x => columns.AddRange(x.Columns));
				}
				
				return columns.Select(counterColumnFormatter).ToList();
			}
		}
		
		protected List<List<Column>> GetNestedColumnList(TInput input)
		{
			Type type = input.GetType();
			
			List<List<Column>> columns = null;
		
			if(type.Equals(typeof(Column)))
			{
				columns = new List<List<Column>>{new List<Column>(new Column[] { input as Column })};
			}
			else if(type.Equals(typeof(SuperColumn)))
			{
				var cols = (input as SuperColumn).Columns;
				if(cols == null) return null;
				columns = new List<List<Column>>{new List<Column>(cols)};
			}
			else if(type.Equals(typeof(List<Column>)))
			{
				var cols = input as List<Column>;
				if(cols.Count == 0) return null;
				columns = new List<List<Column>>{cols};
			}
			else if(type.Equals(typeof(List<SuperColumn>)))
			{
				var superColumns = (input as List<SuperColumn>);
				columns = superColumns.ConvertAll(x => x.Columns);
			}
			
			return columns;
		}
		
		protected Dictionary<TKey, List<Column>> GetNestedColumnDictionary<TKey>(TInput input)
		{
			Dictionary<TKey, List<Column>> columns = null;
			
			Type type = input.GetType();
			
			if(type.Equals(typeof(Column)))
			{
				columns = new Dictionary<TKey, List<Column>>{{default(TKey), new List<Column>(new Column[] { input as Column })}};
			}
			else if(type.Equals(typeof(SuperColumn)))
			{
				var superColumn = (input as SuperColumn);
				var cols = superColumn.Columns;
				if(cols == null) return null;
				columns = new Dictionary<TKey, List<Column>>{{MappingUtil.MappingValueType<TKey>(superColumn.Name), new List<Column>(cols)}};
			}
			else if(type.Equals(typeof(List<Column>)))
			{
				var cols = input as List<Column>;
				if(cols.Count == 0) return null;
				columns = new Dictionary<TKey, List<Column>>{{default(TKey), cols}};
			}
			else
			{
				columns = new Dictionary<TKey, List<Column>>();
				var superColumns = (input as List<SuperColumn>);
				superColumns.ForEach(x => columns.Add(MappingUtil.MappingValueType<TKey>(x.Name), x.Columns));
			}
			
			return columns;
		}


		protected Dictionary<TKey, List<T>> GetNestedNameListDictionary<TKey, T>(TInput input)
		{
			return GetNestedDictionary<TKey, T>(input,
			                                    x => MappingUtil.MappingValueType<T>(x.Name),
			                                    y => MappingUtil.MappingValueType<T>(y.Name));
			                                  
		}
		
		protected Dictionary<TKey, List<T>> GetNestedValueListDictionary<TKey, T>(TInput input)
		{
			return GetNestedDictionary<TKey, T>(input,
			                                    x => MappingUtil.MappingValueType<T>(x.Value),
			                                    y => (T)(object)y.Value);
			                                  
		}
		
		protected Dictionary<TKey, List<KeyValuePair<TDictionaryKey, TDictionaryValue>>> GetNestedKeyValuePairListDictionary<TKey, TDictionaryKey, TDictionaryValue>(TInput input)
		{
			return GetNestedDictionary<TKey, KeyValuePair<TDictionaryKey, TDictionaryValue>>(input,
							                   	x => new KeyValuePair<TDictionaryKey, TDictionaryValue>(MappingUtil.MappingValueType<TDictionaryKey>(x.Name), MappingUtil.MappingValueType<TDictionaryValue>(x.Value)),
							                   	y => new KeyValuePair<TDictionaryKey, TDictionaryValue>(MappingUtil.MappingValueType<TDictionaryKey>(y.Name), (TDictionaryValue)(object)y.Value));
			                                  
		}
		
		private Dictionary<TKey, List<T>> GetNestedDictionary<TKey, T>(TInput input, Func<Column, T> columnFormatter, Func<CounterColumn, T> counterColumnFormatter)
		{
			Type type = input.GetType();
			
			if(type.Equals(typeof(Column)) ||
			   type.Equals(typeof(SuperColumn)) ||
			   type.Equals(typeof(List<Column>)) ||
			   type.Equals(typeof(List<SuperColumn>)))
			{
				Dictionary<TKey, List<Column>> columns = null;
				
				if(type.Equals(typeof(Column)))
				{
					columns = new Dictionary<TKey, List<Column>>{{default(TKey), new List<Column>(new Column[] { input as Column })}};
				}
				else if(type.Equals(typeof(SuperColumn)))
				{
					var superColumn = (input as SuperColumn);
					var cols = superColumn.Columns;
					if(cols == null) return null;
					columns = new Dictionary<TKey, List<Column>>{{MappingUtil.MappingValueType<TKey>(superColumn.Name), new List<Column>(cols)}};
				}
				else if(type.Equals(typeof(List<Column>)))
				{
					var cols = input as List<Column>;
					if(cols.Count == 0) return null;
					columns = new Dictionary<TKey, List<Column>>{{default(TKey), cols}};
				}
				else
				{
					columns = new Dictionary<TKey, List<Column>>();
					var superColumns = (input as List<SuperColumn>);
					superColumns.ForEach(x => columns.Add(MappingUtil.MappingValueType<TKey>(x.Name), x.Columns));
				}
				
				return columns.ToDictionary(kv => kv.Key, kv => kv.Value.Select(columnFormatter).ToList());
			}
			else
			{
				Dictionary<TKey, List<CounterColumn>> columns = null;
				
				if(type.Equals(typeof(CounterColumn)))
				{
					columns = new Dictionary<TKey, List<CounterColumn>>{{default(TKey), new List<CounterColumn>(new CounterColumn[] { input as CounterColumn })}};
				}
				else if(type.Equals(typeof(CounterSuperColumn)))
				{
					var superColumn = (input as CounterSuperColumn);
					var cols = superColumn.Columns;
					if(cols == null) return null;
					columns = new Dictionary<TKey, List<CounterColumn>>{{MappingUtil.MappingValueType<TKey>(superColumn.Name), new List<CounterColumn>(cols)}};
				}
				else if(type.Equals(typeof(List<CounterColumn>)))
				{
					var cols = input as List<CounterColumn>;
					if(cols.Count == 0) return null;
					columns = new Dictionary<TKey, List<CounterColumn>>{{default(TKey), cols}};
				}
				else
				{
					columns = new Dictionary<TKey, List<CounterColumn>>();
					var superColumns = (input as List<CounterSuperColumn>);
					superColumns.ForEach(x => columns.Add(MappingUtil.MappingValueType<TKey>(x.Name), x.Columns));
				}
				
				return columns.ToDictionary(kv => kv.Key, kv => kv.Value.Select(counterColumnFormatter).ToList());
			}
		}
	}
}
